#include <stdio.h>
#include <unistd.h>
#include <string.h>

#include "auth.h"

#include "fingerdb.h"
#include "launcherd.h"


/************************************
if username == NULL, then search all matches
if username != NULL, then search match for specific user

return
	positive as finger id which match
	0 if no match found
	negative on error
************************************/
int authSearchMatch( unsigned char* fingerImage, char* username ){
	
	int res;
	int foundId;
	int currentId;
	int currentThreshold;
	char currentUsername[40];
	void* db;
	struct xyt_packed probe[NBIS_XYT_MAX_COUNT];
	int probeCount;
	struct xyt_packed gallery[NBIS_XYT_MAX_COUNT];
	int galleryCount;
	
	foundId = 0;
	
	//find minutiae in probe
	probeCount = nbisPgmToMinutiae( fingerImage, probe );
	if( probeCount < 0 ){
		printf( "Probe to minutiae error\n" );
		return -1;
	}
	
	//open finger db
	db = fingerdbOpen();
	if( !db ){
		printf( "Fingerdb open error\n" );
		return -2;
	}
	
l_nextFinger:
	currentId = fingerdbSearchNextId( db );
	if( !currentId ) goto l_close;//end of db
	
	//is username is set, then check it match
	if( username ){
		res = fingerdbGetField( currentId, "user", currentUsername, 32 );
		if( res < 1 ) goto l_nextFinger;
		currentUsername[res] = 0x00;//string must be null-terminated
		if( strcmp(username,currentUsername) ) goto l_nextFinger;
	}
	
	//read threshold
	res = fingerdbGetField( currentId, "threshold", &currentThreshold, sizeof(currentThreshold) );
	if( res != sizeof(currentThreshold) ) goto l_nextFinger;
	if( currentThreshold < 1 ) goto l_nextFinger;
	
	//read packed minutiae
	res = fingerdbGetField( currentId, "xyt_packed", gallery, NBIS_XYT_MAX_COUNT * sizeof(*gallery) );
	if( res < 1 ){
		printf( "WARNING: Finger %i has not minutiae!\n", currentId );
		goto l_nextFinger;
	}
	
	//calculate num of gallery minutia
	galleryCount = res / sizeof( *gallery );
	
	//check match score
	if( nbisBozorth3Packed(probe, probeCount, gallery, galleryCount) < currentThreshold ) goto l_nextFinger;
	
	foundId = currentId;
	
l_close:
	fingerdbClose( db );
	
	return foundId;
}


void authFingerTrigger( struct driverInstance* this ){
	
	int res;
	int fingerId;
	struct launcherdRequest rq;
	
	//search any match in finger DB
	fingerId = authSearchMatch( this->data, NULL ); 
	if( fingerId < 1 ){
		printf( "[trigger] No match found\n" );
		goto l_authTrigger;
	}
	
	printf( "[trigger] detected finger %i\n", fingerId );
	
	//get finger owner
	res = fingerdbGetField( fingerId, "user",   &rq.username, sizeof(rq.username)-1 );
	if( res < 1 ){
		printf( "[trigger] Finger %i has not user.\n", fingerId );
		goto l_authTrigger;
	}
	
	//if passive auth enabled check match username
	if( this->passiveAuthUsername ){
		if( !strcmp(this->passiveAuthUsername, rq.username) ){
			this->passiveAuthModeIndicator = PA_SUCCESS;
			goto l_PA_send_answer;
		}
	}
	
	//get finger action
	res = fingerdbGetField( fingerId, "action", &rq.programm, sizeof(rq.programm)-1 );
	if( res < 1 ){
		printf( "[trigger] Finger %i has not action.\n", fingerId );
		goto l_authTrigger;
	}
	
	//strings must be null-terminated
	rq.username[sizeof(rq.username)-1] = 0x00;
	rq.programm[sizeof(rq.programm)-1] = 0x00;
	
	//send command to launcherd
	if( write(this->launcherdSocket, &rq, sizeof(rq)) != sizeof(rq) ){
		perror( "[trigger] Send request to launcherd failed" );
	}
	
	return;
	
l_authTrigger:
	//if passive auth enabled, then send auth failed
	if( this->passiveAuthUsername ){
		this->passiveAuthModeIndicator = PA_FAILED;
		
l_PA_send_answer:
		
		this->driverState = STATE_WAIT_IPC;
		sem_post( &this->semaphoreIpc );
		sem_wait( &this->semaphoreScan );
		this->driverState = STATE_RUNNING;
	}
}

/********************************
func authEnroll
	register new fingerprint
input
	xyts - pointer to minutiae
	xyt_count - number of minutiae
return
	negative on error
	positive as new finger id
********************************/
int authEnroll( struct xyt_packed* xyts, int xyt_count ){
	
	int res;
	int new_id;
	
	//create new record in the DB
	new_id = fingerdbCreateFinger();
	if( new_id < 0 ){
		printf( "%s failed to create new record in db\n", __FUNCTION__ );
		return -2;
	}
	
	//save minutiae in the DB
	res = fingerdbSetField( new_id, "xyt_packed", xyts, sizeof(*xyts) * xyt_count );
	if( res ){
		printf( "%s save minutiae error\n", __FUNCTION__ );
		return -3;
	}
	
	return new_id;
}


/**************************************************************
func authTest
	calculate match score between exist and provided fingerprint
input
	fingerId - existing finger id (gallery)
	fingerImage - provided finger image (probe)
return
	negative on error
	match score
***************************************************************/
int authTest( int fingerId, unsigned char* fingerImage ){
	
	struct xyt_packed gallery[NBIS_XYT_MAX_COUNT];
	struct xyt_packed probe[NBIS_XYT_MAX_COUNT];
	int galleryCount;
	int probeCount;
	
	//check id exist
	if( !fingerdbCheckIdExist(fingerId) ){
		printf( "%s) Fingerdb has not record with id %i.\n", __FUNCTION__, fingerId );
		return -1;
	}
	
	//open gallery packed minutia
	galleryCount = fingerdbGetField( fingerId, "xyt_packed", gallery, sizeof(*gallery) * NBIS_XYT_MAX_COUNT );
	if( galleryCount <= 0 ){
		printf( "%s) Load gallery error.\n", __FUNCTION__ );
		return -2;
	}
	//convert size to count
	galleryCount /= sizeof(struct xyt_packed);
	
	//convert finger image to minutiae
	probeCount = nbisPgmToMinutiae( fingerImage, probe );
	if( probeCount < 0 ) return -3;
	
	return nbisBozorth3Packed( probe, probeCount, gallery, galleryCount );
}
